#pragma once

#include <list>
#include "GeometricDefinitions.h"
#include <map>


namespace Putin{

#define DynamicType 1
#define StaticType 2
#define ProjectileType 3

class Wrapper; //Forward declaration
class CollisionDetection;
class PhysicsObject;
class DynamicPhysicsObject;
class StaticPhysicsObject;

/**
 * Helper class for the Sweep and prune algorithm
 * Each wrapper (object) has 4 nodes, two for the x-axis and two for y-axis
 * A node defines either the beggining or the end of an objects AABB and stores
 * a pointer to its owner object
 */
class Node
{
public:
    Node(Wrapper *wrapper, bool begin);	
	bool mBegin;
    float mValue;
    Wrapper *mWrapper;
};

/**
 * Helper class for the Sweep and prune algorithm
 * The wrapper is linked to a geometry object from where it updates AABB data in nodes
 * Every wrapper also has a list where possible collisions for every frame is stored
 */
class Wrapper
{
public:	
    Wrapper(Geometry *geo);
	virtual ~Wrapper(void);

	/**
	 * Update AABBs with data from geometry object (done every frame)
	 */
    virtual void Update(void);

	Node *mXbegin;
    Node *mXend;
    Node *mYbegin;
    Node *mYend;    
	Geometry *mGeometry;
	std::list<Geometry*> mColliders;
 };


/**
 * Following three structs are aid list functions in the Sweep and prune object
 * 1st. Simple larger than operation for two nodes
 * 2nd. Checks if a Wrapper has been disposed
 * 3rd. Checks if a Node has been disposed
 */
struct SortNodes : public std::binary_function<Node*, Node*, bool>
{
	bool operator()(Node* left, Node* right) const;
};

struct RemoveDisposedWrapper : public std::unary_function<Wrapper*, bool>
{
	bool operator()(Wrapper* w) const;
};

struct RemoveDisposedNode : public std::unary_function<Node*, bool>
{
	bool operator()(Node* n) const;
};

/*
 * This class allows a function pointer to be used in the SAP-class
 * We add a pointer to our CollisionDetector to be able to return results
 */
class CollisionFunction
{
	public:
	virtual void execute(Wrapper* w1, Wrapper* w2, CollisionDetection* coldet) = 0;
};

/*
 * when there is a collsion along the first axis
 * and if there is no early fail conditions then they are
 * added to each others colliders list
 */
class HandleFirstCollision : public CollisionFunction
{
public: 
	virtual void execute(Wrapper* w1, Wrapper* w2, CollisionDetection* coldet);
};

/*
 * when there is a collision along the second axis then 
 * it checks to see if there was a collision along the first. 
 * if there is then the 2 geometries bounding boxes are colliding.
 */
class HandleSecondCollision : public CollisionFunction
{
public: 
	virtual void execute(Wrapper* w1, Wrapper* w2, CollisionDetection* coldet);
};


/*
 * Main collision detection object
 * Call Update every frame, if there is a possible collision this will be flagged in
 * the previous method, HandleSecondCollision
 */
class CollisionDetection
{
public:
	virtual ~CollisionDetection(void);

	/*
	 * Do this for every object that is to be checked for collision
	 */
    virtual void AddGeometry(Geometry* item);

	/*
	 * Call this to delete removed objects from the lists
	 */
    virtual void RemoveDisposed(void);

	/*
	 * Run sweep and prune
	 */
    virtual void Run(bool collisionson);

	/*
	 * Calls update() in all wrappers
	 */
    virtual void Update();

	/*
	 * Check an axis for collisions
	 */	
	virtual void RunAxis(std::list<Node*> list, CollisionFunction* collisionFunction);

	virtual void RunList();
	virtual void CollideDynamicVsProjectile(PhysicsObject* po1, PhysicsObject* po2);
	virtual void CollideDynamicVsDynamic(PhysicsObject* po1, PhysicsObject* po2);
	virtual void CollideDynamicVsStatic(PhysicsObject* po1, PhysicsObject* po2);
	/*
	 *	Following functions handle OBB collision detection
	 *	Call intersect to check for collision
	 */
	virtual bool Overlap(float min0, float max0, float min1, float max1);
	virtual bool SpanOverlap(float min0, float max0, float min1, float max1);
	virtual void ComputeSpan(Vec3f Axis, OrientedBoundingBox Box, float& min, float& max);
	virtual bool AxisOverlap(Vec3f Axis, OrientedBoundingBox B0, OrientedBoundingBox B1);
	virtual bool Intersect(OrientedBoundingBox B0, OrientedBoundingBox B1);

	std::list<Wrapper*> mWrappers;
    std::list<Node*> mXlist;
    std::list<Node*> mYlist;
	std::map<PhysicsObject*, PhysicsObject*> mCollidingObjects; 
	bool mCollisionsOn;
};

}//End namespace Putin